package com.zhz.mall.common.util.ccollect;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ArrayUtil;
import com.google.common.base.Functions;
import com.zhz.mall.common.util.FunctionUtils;
import com.zhz.mall.common.util.NumberUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * CollStreamUtil
 * <p>
 * 集合的 stream 操作封装
 *
 * @author zhouhengzhe
 */
public abstract class CollStreamUtil {


    public static <E, T> List<T> toList(Collection<E> collection, Function<E, T> function) {
        return toList(collection, function, Objects::nonNull);
    }


    public static <E, T> List<T> toList(Collection<E> collection, Function<E, T> function, Predicate<? super T> filter) {
        if (CollUtil.isEmpty(collection)) {
            return Collections.emptyList();
        }
        return collection.stream().map(function).filter(filter).collect(Collectors.toList());
    }


    public static <E, T> Set<T> toSet(Collection<E> collection, Function<E, T> function) {
        return toSet(collection, function, Objects::nonNull);
    }


    public static <E, T> Set<T> toSet(Collection<E> collection, Function<E, T> function, Predicate<? super T> filter) {
        if (CollUtil.isEmpty(collection)) {
            return Collections.emptySet();
        }
        return collection.stream().map(function).filter(filter).collect(Collectors.toSet());
    }


    public static <E, T> List<T> transform(List<E> collection, Function<E, T> function) {
        return toList(collection, function);
    }


    public static <E, T> Set<T> transform(Set<E> collection, Function<E, T> function) {
        return toSet(collection, function);
    }


    public static <E> List<E> filter(Collection<E> collection, Predicate<E> filterFunc) {
        if (CollUtil.isEmpty(collection)) {
            return Collections.emptyList();
        }
        return collection.stream().filter(filterFunc).collect(Collectors.toList());
    }


    public static <E> E findFirst(E[] array, Predicate<E> filterFunc) {
        if (ArrayUtil.isEmpty(array)) {
            return null;
        }
        return Stream.of(array).filter(filterFunc).findFirst().orElse(null);
    }


    public static <E> E findFirst(Collection<E> collection, Predicate<E> filterFunc) {
        if (CollUtil.isEmpty(collection)) {
            return null;
        }
        return collection.stream().filter(filterFunc).findFirst().orElse(null);
    }


    public static <E> boolean exists(Collection<E> collection, Predicate<E> filterFunc) {
        return findFirst(collection, filterFunc) != null;
    }


    @SuppressWarnings("unchecked")
    public static <E, T extends E> T findFirst(Collection<E> collection, Class<T> targetType) {
        return (T) findFirst(collection, (targetType::isInstance));
    }


    public static <E, K> Map<K, List<E>> groupByKey(Collection<E> collection, Function<E, K> keyFunc) {
        if (CollUtil.isEmpty(collection)) {
            return Collections.emptyMap();
        }
        return collection.stream().collect(Collectors.groupingBy(keyFunc, Collectors.toList()));
    }


    public static <E, K> Map<K, Long> countingByKey(Collection<E> collection, Function<E, K> keyFunc) {
        if (CollUtil.isEmpty(collection)) {
            return Collections.emptyMap();
        }
        return collection.stream().collect(Collectors.groupingBy(keyFunc, Collectors.counting()));
    }


    public static <E, K> LinkedHashMap<K, Long> countingByKey(Collection<E> collection, Function<E, K> keyFunc, boolean asc) {
        Map<K, Long> countingMap = countingByKey(collection, keyFunc);
        Comparator<Map.Entry<K, Long>> comparator = Comparator.comparing(Map.Entry::getValue);
        if (!asc) {
            comparator = comparator.reversed();
        }
        return CollUtil.sortByEntry(countingMap, comparator);
    }


    /**
     * collection 转为 map
     */
    public static <E, K, V> Map<K, V> toMap(Collection<E> collection, Function<E, K> keyFunc, Function<E, V> valueFunc) {
        return toMap(collection, keyFunc, valueFunc, false);
    }


    public static <E, K> Map<K, E> toMap(Collection<E> collection, Function<E, K> keyFunc) {
        return toMap(collection, keyFunc, Functions.identity());
    }


    /**
     * collection 转为 map
     *
     * @param collection          需要转化的集合
     * @param keyFunc             key 转换表达式
     * @param valueFunc           value 转换表达式
     * @param ignoreConflictValue 当多个 value 映射到同一个 key 时，是否忽略掉冲突的 value
     * @return 转化后的map
     */
    public static <E, K, V> Map<K, V> toMap(Collection<E> collection, Function<E, K> keyFunc, Function<E, V> valueFunc, boolean ignoreConflictValue) {
        if (CollUtil.isEmpty(collection)) {
            return Collections.emptyMap();
        }
        Collector<E, ?, Map<K, V>> mapCollector = Collectors.toMap(keyFunc, valueFunc);
        if (ignoreConflictValue) {
            mapCollector = Collectors.toMap(keyFunc, valueFunc, FunctionUtils.first());
        }
        return collection.stream().collect(mapCollector);
    }


    public static <E, K> Map<K, E> toMap(Collection<E> collection, Function<E, K> keyFunc, boolean ignoreConflictValue) {
        return toMap(collection, keyFunc, Functions.identity(), ignoreConflictValue);
    }


    public static <E> void forEach(Collection<E> collection, Predicate<E> filterFunc, Consumer<? super E> action) {
        if (CollUtil.isEmpty(collection)) {
            return;
        }
        collection.stream().filter(filterFunc).forEach(action);
    }

    public static <E> void forEach(Collection<E> collection, Consumer<? super E> action) {
        forEach(collection, Objects::nonNull, action);
    }


    /**
     * sum 求和
     *
     * @param collection 数据
     * @param filter     过滤器
     * @param mapper     mapper
     * @param scale      保留小数位数
     */
    public static <E> BigDecimal sum(Collection<E> collection, Predicate<E> filter, Function<E, BigDecimal> mapper, int scale) {
        if (CollUtil.isEmpty(collection)) {
            return BigDecimal.ZERO;
        }

        BigDecimal sum = collection.stream().filter(e -> Objects.nonNull(e) && filter.test(e))
                .map(mapper).filter(Objects::nonNull).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
        return NumberUtils.round(sum, scale);
    }


    /**
     * sum 求和（保留 2 位小数）
     */
    public static <E> BigDecimal sum(Collection<E> collection, Predicate<E> filter, Function<E, BigDecimal> mapper) {
        return sum(collection, filter, mapper, 2);
    }


    /**
     * sum 求和（保留 2 位小数）
     */
    public static <E> BigDecimal sum(Collection<E> collection, Function<E, BigDecimal> mapper) {
        return sum(collection, Objects::nonNull, mapper);
    }


    /**
     * 集合转为数组
     */
    public static String[] toArray(Collection<String> collection) {
        Assert.notNull(collection);
        return collection.toArray(new String[0]);
    }


    /**
     * 查找重复的元素
     */
    public static <E> Set<E> findDistinct(Collection<E> collection) {
        if (CollUtil.isEmpty(collection)) {
            return Collections.emptySet();
        }

        return collection.stream()
                .collect(Collectors.toMap(e -> e, e -> 1, Integer::sum))
                .entrySet()
                .stream()
                .filter(e -> e.getValue() > 1)
                .map(Map.Entry::getKey)
                .collect(Collectors.toSet());
    }


}
